﻿using Bouyei.GeoCore.Geometries;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Bouyei.GeoCore.GeoParsers.Wkb
{
    public class GeometryWriter:BaseBytes
    {
        private bool isHasEndian = true;

        public GeometryWriter(ByteOrder byteOrder = ByteOrder.LittleEndian, bool isHasEndian = true)
            : base(null, byteOrder, 0)
        {
            this.isHasEndian = isHasEndian;
        }

        public unsafe byte[] Writer(Geometry geometry)
        {
            switch (geometry.GeometryType)
            {
                case GeoType.POINT:
                    {
                        int size = 4 + 16;
                        if (isHasEndian) size += 1;

                        Buffer = new byte[size];
                        if (isHasEndian) Buffer[0] = (byte)ByteOrder;
                    }
                    break;
                case GeoType.MULTIPOINT:
                    {
                        int pCount = geometry.GetSequence(0, 0).Count;
                        int size = 4 + 4 + 16 * pCount;
                        if (isHasEndian) size += 1;

                        Buffer = new byte[size];
                        if (isHasEndian) Buffer[0] = (byte)ByteOrder;
                    }
                    break;
                case GeoType.LINESTRING:
                    {
                        int pCount = geometry.GetSequence(0, 0).Count;
                        int size = 4 + 4 + 16 * pCount;//geotype+count+xy[][]

                        if (isHasEndian) size += 1;
                        Buffer = new byte[size];

                        if (isHasEndian) Buffer[0] = (byte)ByteOrder;
                    }
                    break;
                case GeoType.MULTILINESTRING:
                    {
                        var seqs = geometry.GetGemoetry(0);
                        int size = 8;

                        for (int i = 0; i < seqs.Count; ++i)
                        {
                            size += 4;
                            for (int j = 0; j < seqs[i].Count; ++j)
                                size += 16;
                        }
                        if (isHasEndian) size += 1;

                        Buffer = new byte[size];
                        if (isHasEndian) Buffer[0] = (byte)ByteOrder;
                    }
                    break;
                case GeoType.POLYGON:
                    {
                        var seqs = geometry.GetGemoetry(0);
                        int size = 8;//geotype+count

                        for (int i = 0; i < seqs.Count; ++i)
                        {
                            size += 4;
                            for (int j = 0; j < seqs[i].Count; ++j)
                                size += 16;
                        }
                        if (isHasEndian) size += 1;

                        Buffer = new byte[size];
                        if (isHasEndian) Buffer[0] = (byte)ByteOrder;
                    }
                    break;
                case GeoType.MULTIPOLYGON:
                    {
                        int size = 8;

                        for (int j = 0; j < geometry.GeometryCount; ++j)
                        {
                            var seqs = geometry.GetGemoetry(j);
                            size += 8;

                            for (int k = 0; k < seqs.Count; ++k)
                            {
                                size += 4;
                                for (int i = 0; i < seqs[k].Count; ++i)
                                    size += 16;
                            }
                        }

                        if (isHasEndian) size += 1;

                        Buffer = new byte[size];
                        if (isHasEndian) Buffer[0] = (byte)ByteOrder;
                    }
                    break;
                default:
                    throw new Exception("not supported:" + geometry.GeometryType.ToString());
            }

            return WriteGeometryBlob(geometry, Buffer, isHasEndian ? 1 : 0);
        }

        public unsafe byte[] WriteGeometryBlob(Geometry geometry, byte[] buffer, int offset = 1)
        {
            switch (geometry.GeometryType)
            {
                case GeoType.POINT:
                    {
                        fixed (byte* ptr = &buffer[offset])
                        {
                            *(int*)(ptr) = (int)geometry.GeometryType;

                            WkbPoint.ToWriter(geometry, ptr + 4);
                        }
                        return buffer;
                    }
                case GeoType.MULTIPOINT:
                    {
                        fixed (byte* ptr = &buffer[offset])
                        {
                            *(int*)(ptr) = (int)geometry.GeometryType;

                            WkbMultiPoint.ToWriter(geometry, ptr + 4);
                        }

                        return buffer;
                    }
                case GeoType.LINESTRING:
                    {
                        fixed (byte* ptr = &buffer[offset])
                        {
                            *(int*)(ptr) = (int)geometry.GeometryType;

                            WkbLineString.ToWriter(geometry, ptr + 4);
                        }

                        return buffer;
                    }
                case GeoType.MULTILINESTRING:
                    {
                        fixed (byte* ptr = &buffer[offset])
                        {
                            *(int*)(ptr) = (int)geometry.GeometryType;

                            WkbMultiLineString.ToWriter(geometry, ptr + 4);
                        }

                        return buffer;
                    }
                case GeoType.POLYGON:
                    {
                        fixed (byte* ptr = &buffer[offset])
                        {
                            *(int*)(ptr) = (int)geometry.GeometryType;

                            WkbPolygon.ToWriter(geometry, ptr + 4);
                        }
                        return buffer;
                    }
                case GeoType.MULTIPOLYGON:
                    {
                        fixed (byte* ptr = &buffer[offset])
                        {
                            *(int*)(ptr) = (int)geometry.GeometryType;

                            WkbMultiPolygon.ToWriter(geometry, ptr + 4);
                        }
                        return buffer;
                    }
                default:
                    throw new Exception("not supported:" + geometry.GeometryType.ToString());
            }
        }
    }
}
